<template>
  <div
    class="report-container"
    @drop="handleDropRow($event)"
    v-loading="isLoading"
    @dragover="handleDragOver($event)"
    :style="`${dialog ? 'width:62%' : ''}`"
  >
    <div class="tool-box" draggable v-rePosition>
      <el-tooltip effect="dark" content="添加布局" placement="top">
        <img
          @click="
            addRow.show = true;
            addCol.show = false;
          "
          width="55"
          class="cur-p add-row-icon"
          src="../../../../static/images/reportMgr/icon/layout.svg"
        />
      </el-tooltip>
      <el-tooltip effect="dark" content="添加组件" placement="top">
        <img
          @click="
            addCol.show = true;
            addRow.show = false;
          "
          width="55"
          class="cur-p add-col-icon"
          src="../../../../static/images/reportMgr/icon/component.svg"
        />
      </el-tooltip>
      <router-link
        v-if="$route.name === 'dragReport'"
        :to="{
          path: '/business/reportmgr/previewReport/' + chartLayoutName,
          query: { isEditPath: true },
        }"
        target="_blank"
      >
        <el-tooltip effect="dark" content="预览" placement="top">
          <img
            @click="handleToPreviewPage"
            v-show="!addCol.show"
            width="55"
            class="cur-p preview-icon"
            src="../../../../static/images/reportMgr/icon/preview.svg"
          />
        </el-tooltip>
      </router-link>
      <el-tooltip effect="dark" content="保存" placement="top">
        <img
          @click="handleSaveLayout"
          width="60"
          class="cur-p preview-icon"
          src="../../../../static/images/reportMgr/icon/save.svg"
        />
      </el-tooltip>
      <el-tooltip effect="dark" content="返回" placement="top">
        <img
          v-if="$route.name !== 'dragReport'"
          @click="handleReturnLayout"
          width="55"
          class="cur-p return-icon"
          src="../../../../static/images/reportMgr/icon/return.svg"
        />
      </el-tooltip>
    </div>
    <div
      v-if="$route.name === 'dragReport'"
      class="flex flex-center mr-center report-title"
    >
      <span v-if="!showTitleEditer" @click="handleEditTitle">{{
        dragReportData.title
      }}</span>
      <el-input
        v-if="showTitleEditer"
        class="report-title-input"
        ref="titleInput"
        v-model="dragReportData.title"
        @keyup.enter="showTitleEditer = false"
        @blur="showTitleEditer = false"
        placeholder="请输入报表名称"
      />
    </div>
    <div class="mr-center report">
      <div
        v-if="showTime"
        style="height: 32px; width: 100%; background-color: #fff"
      ></div>
      <div v-show="showLineR" class="auxiliary-line ri"></div>
      <div v-show="showLineB" class="auxiliary-line bo"></div>
      <div
        class="layout-row"
        v-for="(row, rowIndex) in dragReportData.children"
        :key="rowIndex"
        :style="rowStyle(row)"
        @drop.stop="pushDropRow($event, row)"
        @dragover="handleDragOver($event, row)"
      >
        <div
          ref="colBox"
          tabindex="0"
          class="ps-r col-box border-r5 layout-col-box"
          :class="{ 'cur-all': !!col.previewImage }"
              v-for="(col, colIndex) in row.children"
          :style="
            previewColStyle(
              {
                col: col,
                componentName: col.componentName,
                width: col.initCol * 1,
                height:
                  (col.colHeight !== undefined
                    ? col.colHeight
                    : row.initHeight) <= 100 &&
                  (col.colHeight || row.initHeight) !== 0
                    ? parseInt(
                        col.colHeight !== undefined
                          ? col.colHeight
                          : row.initHeight
                      )
                    : (col.colHeight !== undefined
                        ? col.colHeight
                        : row.initHeight) == 0
                    ? 'auto'
                    : parseInt(col.colHeight || row.initHeight) + 56,
                baseHeight: 1,
              },
              {
                backgroundSize:
                  row.initHeight == 0
                    ? '100% 300px'
                    : '100% ' + row.initHeight + 'px',
              }
            )
          "
          :key="colIndex"
          :draggable="col.componentName"
          @dragstart.stop="handleDragCol($event, col)"
          @drop.stop="handleDropCol($event, col, row)"
          @dragover.stop="handleDragOver($event, col)"
          v-resize="col"
        >
          <el-button
            v-if="
              colIndex === row.children.length - 1 &&
              !row.children.filter((item) => item.componentName).length
            "
            class="removeButton"
            plain
            size="medium"
            @click="handleRemoveRow(row)"
            >删除行
          </el-button>

          <div
            draggable="true"
            @dragstart.stop="resizeStart"
            @drag.stop="dragRight($event, col)"
            class="resize-border ri"
          ></div>

          <div
            draggable="true"
            @dragstart.stop="resizeStart"
            @drag.stop="dragBottom($event, col)"
            class="resize-border bo"
          ></div>

          <el-button
            v-if="col.componentName && col.dataKey === 'common'"
            class="btn-icon el-icon-edit cur-p edit"
            @click="setStyle(col, row)"
          ></el-button>

          <el-button
            v-if="col.componentName"
            class="btn-icon el-icon-delete cur-p remove"
            @click.stop="resetCol(col)"
          ></el-button>

          <div
            class="col-title"
            v-if="
              col.componentName !== 'title-title' &&
              col.componentName !== 'text-description'
            "
          >
            <span class="main-front">{{ col.mainTitle }}</span>
            <span class="secondary-front"
              >&nbsp;&nbsp; {{ col.secondTitle }}</span
            >
            <span
              v-if="
                col.dataKey === 'common' && col.componentData.url.length > 0
              "
              class="detail-btn"
              @click.stop="
                changeRoute(col.componentData ? col.componentData.url : [])
              "
              >{{ col.componentData.linkText || "详情" }}</span
            >
          </div>
          <default-container v-if="col.componentName">
            
            <component
              v-if="!Array.isArray(col.api) || col.api.length === 1"
              :mainTitle="col.mainTitle"
              :is="`custom-report-custom-report-component-${col.componentName}`"
              :dataKey="col.dataKey"
              :componentData="col.componentData"
              :api="!Array.isArray(col.api) ? col.api : col.api[0]"
              :method="col.method || 'get'"
              :changeSize="changeSize"
              :changeData="changeData"
              :interval="col.interval"
              @changeTableProp="changeTableProp(col.componentData, $event)"
              @changeTitleTooltip="
                changeTitleTooltip(col.componentData, $event)
              "
              @changeSeriesTypeList="
                changeSeriesTypeList(col.componentData, $event)
              "
              @changeSeriesYAxisIndexList="
                changeSeriesYAxisIndexList(col.componentData, $event)
              "
              @changeBarSeriesStackList="
                changeBarSeriesStackList(col.componentData, $event)
              "
              @changeColYAxis="changeColYAxis(col.componentData, $event)"
              @changeBarLabelList="
                changeBarLabelList(col.componentData, $event)
              "
              :mixinParam="mixinParam"
            />
            <el-tabs
              v-if="Array.isArray(col.api) && col.api.length > 1"
              type="border-card"
              tab-position="bottom"
              @tab-click="changeComTab($event, col)"
            >
              <el-tab-pane
                v-for="(item, apiIndex) in col.api"
                :key="apiIndex"
                :label="item.label ? item.label : ''"
              >
                <component
                  :key="col.componentName + col.dataKey + comKey"
                  :mainTitle="col.mainTitle"
                  :is="`custom-report-custom-report-component-${col.componentName}`"
                  :dataKey="col.dataKey"
                  :componentData="col.componentData"
                  :api="col.api[apiIndex]"
                  :method="col.method || 'get'"
                  :changeSize="changeSize"
                  :changeData="changeData"
                  :interval="col.interval"
                  @changeTableProp="changeTableProp(col.componentData, $event)"
                  @changeTitleTooltip="
                    changeTitleTooltip(col.componentData, $event)
                  "
                  @changeSeriesTypeList="
                    changeSeriesTypeList(col.componentData, $event)
                  "
                  @changeSeriesYAxisIndexList="
                    changeSeriesYAxisIndexList(col.componentData, $event)
                  "
                  @changeBarSeriesStackList="
                    changeBarSeriesStackList(col.componentData, $event)
                  "
                  @changeColYAxis="changeColYAxis(col.componentData, $event)"
                  @changeBarLabelList="
                    changeBarLabelList(col.componentData, $event)
                  "
                  :mixinParam="mixinParam"
                />
              </el-tab-pane>
            </el-tabs>
          </default-container>
        </div>
      </div>
    </div>

    <el-drawer
      title="布局选择"
      v-model="addRow.show"
      size="50%"
      direction="ltr"
      custom-class="demo-drawer"
      ref="drawer"
      modal-class="opacityModel"
    >
      <default-layout-editor @drag-row-start="handleDragNewRow" />
    </el-drawer>
    <el-drawer
      title="组件选择"
      v-model="addCol.show"
      size="50%"
      direction="ltr"
      custom-class="demo-drawer"
      ref="drawer"
      modal-class="opacityModel"
    >
      <default-framework
        :chartLayoutName="chartLayoutName"
        ref="framework"
        @drag-col-start="handleDragNewCol"
      />
    </el-drawer>
    <el-drawer
      title="组件样式编辑"
      v-model="dialog"
      size="38%"
      destroy-on-close
      direction="rtl"
      custom-class="demo-drawer"
      ref="drawer"
      @close="resize"
      modal-class="opacityModel"
    >
      <div class="demo-drawer__content">
        <editComStyle
          @moveRow="moveRow"
          @removeRow="handleRemoveRow"
          :rowData="rowData"
          :colData="colData"
          @changeSize="resize"
          @changeForm="changeForm"
          :rowCount="dragReportData.children.length"
          :menuData="menuData"
          @change-api="changeApi"
          @changeType="changeType"
        >
        </editComStyle>
        <div class="demo-drawer__footer">
          <el-button style="float: left" size="medium" @click="saveComponent"
            >保存模板
          </el-button>
          <el-button
            size="medium"
            type="primary"
            @click="$refs.drawer.closeDrawer()"
          >
            确 定</el-button
          >
        </div>
      </div>
    </el-drawer>
  </div>
</template>

<script>
import customReports from "../previewReport/index.js";
import { deepClone, setStorage, diffIndex, mixinObjs } from "../utils";
import { getInitRow, getInitCol, resetIndex } from "../utils/dragReport";
import defaultFramework from "../components/default-framework/index.vue";
import defaultLayoutEditor from "../components/default-layout-editor/index.vue";
import nt from "../../network";
import defaultContainer from "../components/custom-report/default-container.vue";
import editComStyle from "./editComStyle.vue";
console.log(customReports)
const colSkipArr = ["initCol", "colHeight", "height", "widthCol"];
let observer = {};
let pageX = 0;
let pageY = 0;
export default {
  name: "dragReport",
  components: {
    defaultFramework,
    defaultLayoutEditor,
    editComStyle,
    defaultContainer,
    ...customReports,
  },
  props: ["chartLayoutName", "mixinParam", "isNlReport", "showTime"],
  directives: {
    rePosition: {
      beforeMount(el, binding, vnode) {
        let x = 0;
        let y = 0;
        let that = binding.instance;
        el.ondragstart = function (e) {
          x = e.pageX - el.offsetLeft;
          y = e.pageY - el.offsetTop;
          $(el).addClass("drag");
          $(el).css({
            transition: "none",
          });
          let dragStyle = new Image({ width: 0, height: 0 });
          e.dataTransfer.setDragImage(dragStyle, 0, 0);
          e.dataTransfer.effectAllowed = "move";
          that.dragData.isResize = true;
        };
        el.ondrag = function (e) {
          that.dragData.isResize = true;
          //阻止默认行为
          e.preventDefault();
          //阻止事件冒泡
          e.stopPropagation();
          let xx = e.pageX - x;
          let yy = e.pageY - y;
          // el.style["left"] = xx + "px"
          el.style["top"] = yy + "px";
        };
        el.ondragover = function (e) {
          e.preventDefault();
          e.stopPropagation();
          that.dragData.isResize = false;
          setTimeout(function () {
            $(el).removeClass("drag");
            $(el).css({
              transition: "all 0.8s",
            });
          }, 1000);
        };
      },
    },
    resize: {
      bind(el, binding, vnode) {
        let MutationObserver =
          window.MutationObserver ||
          window.WebKitMutationObserver ||
          window.MozMutationObserver;
        let that = vnode.context;
        // 创建观察者对象
        // 图形拖拽时防抖
        const debounce = (() => {
          let timer = null;
          return (fun, delay = 100) => {
            clearTimeout(timer);
            timer = setTimeout(() => fun(), delay);
          };
        })();
        //请原谅我Jquery和DOM原生方法一起使用……致后来的开发者
        observer = new MutationObserver(function (mutations) {
          if (
            that.isDropRow ||
            that.dragData.col ||
            that.isMutationObserver > 2
          ) {
            //isMutationObserver暂时不用了
            debounce(() => {
              that.isDropRow = false;
              that.dragData.col = null;
              that.isMutationObserver = 0;
            });
            return;
          }
          mutations.forEach(function (mutation) {
            let resizeDom = $(mutation.target);
            $(".auxiliary-line.ri").css({
              left:
                ((resizeDom.innerWidth() + resizeDom.position().left + 7.5) /
                  $(".layout-row").width()) *
                  100 +
                "%",
            });
            $(".auxiliary-line.bo").css({
              top: resizeDom.innerHeight() + resizeDom.position().top + 5,
            });
            debounce(() => {
              //拖拽新row时也会触发导致bug，所以做层判断,右侧抽屉打开会压缩左侧空间上下移动row也会bug，所以dialog也做判断
              if (
                mutations.length > 2 ||
                that.dialog ||
                (mutations.length === 2 &&
                  mutations[0].target !== mutations[1].target)
              ) {
                return;
              }
              let width = resizeDom.outerWidth();
              let height = resizeDom.outerHeight();
              let colData = binding.value;
              let distance = that.adhesionDistance;
              let widthCha = 0;
              let heightCha = 0;
              let isTitleBox = false;
              let isResize = false;
              let marPadBorWid = 0;
              let titlePadBorHei = 0;
              //主要是判断是不是padding：0，如果是则加上17，不是加上57
              if (
                colData.componentName !== "title-title" &&
                colData.componentName !== "text-description"
              ) {
                if (colData.componentName) {
                  marPadBorWid = 57;
                  titlePadBorHei = 14 + 56;
                } else {
                  if (height - 2 <= 100 && height != 0) {
                    marPadBorWid = 17;
                    titlePadBorHei = 2;
                    isTitleBox = true;
                  } else {
                    marPadBorWid = 57;
                    titlePadBorHei = 14 + 56;
                  }
                }
              } else {
                if (height - 2 <= 100 && height != 0) {
                  titlePadBorHei = 2;
                } else {
                  titlePadBorHei = 2 + 56;
                }
                isTitleBox = true;
              }
              //index用来判断需要比较的宽高是不是原来自己的宽高，解决微调尺寸时抖动bug
              let index = $(mutation.target).index();
              that.widthList.forEach((item, i) => {
                if (index !== i) {
                  let cha =
                    mutation.target.offsetLeft +
                    mutation.target.offsetWidth -
                    item;
                  cha = Math.abs(cha);
                  if (cha < distance) {
                    that.showLineR = true;
                    if (!widthCha || cha < widthCha) {
                      widthCha = cha;
                      width = item - mutation.target.offsetLeft;
                    }
                  }
                }
              });
              that.heightList.forEach((item, i) => {
                if (index !== i) {
                  let cha =
                    mutation.target.offsetTop +
                    mutation.target.offsetHeight -
                    item;
                  cha = Math.abs(cha);
                  if (cha < distance) {
                    that.showLineB = true;
                    if (!heightCha || cha < heightCha) {
                      heightCha = cha;
                      height = item - mutation.target.offsetTop;
                      isResize = true;
                    }
                  }
                }
              });
              colData.height = height - titlePadBorHei;
              colData.widthCol =
                24 * ((width + 15) / $(".layout-row").width()).toFixed(6);
              colData.initCol = colData.widthCol || colData.initCol;
              that.$set(colData, "initCol", colData.initCol);
              colData.colHeight = colData.height
                ? colData.height
                : colData.colHeight;
              colData.styleModuleId && that.setElChangeType(colData, "edit");
              let col = colData;
              let style = that.previewColStyle({
                col: col,
                componentName: col.componentName,
                width: col.initCol * 1,
                height:
                  (col.componentName === "title-title" &&
                    col.componentName !== "text-description") ||
                  (col.colHeight <= 100 && col.colHeight == 0)
                    ? col.colHeight * 1
                    : col.colHeight == 0
                    ? "auto"
                    : col.colHeight * 1 + 56,
                baseHeight: 1,
              });
              $(mutation.target).css(style);
              delete colData.height;
              delete colData.widthCol;
              that.isMutationObserver++;
              that.$nextTick(() => {
                $(".auxiliary-line.ri").css({
                  left:
                    ((resizeDom.innerWidth() +
                      resizeDom.position().left +
                      7.5) /
                      $(".layout-row").width()) *
                      100 +
                    "%",
                });
                $(".auxiliary-line.bo").css({
                  top: resizeDom.innerHeight() + resizeDom.position().top + 5,
                });
                that.markPosition();
              });
              setTimeout(() => {
                that.showLineR = false;
                that.showLineB = false;
              }, 5000);
              that.resize();
            });
          });
        });
        // 配置观察选项:
        let config = {
          attributeFilter: ["style"],
          attributeOldValue: true,
          attributes: true, //检测属性变动
        };
        // 传入目标节点和观察选项
        observer.observe(el, config);
      },
      unbind(el, binding) {
        // /停止观察
        observer.disconnect();
      },
    },
  },
  data() {
    return {
      x: 0,
      y: 0,
      domWidth: 0,
      domHeight: 0,
      isMutationObserver: 0, //MutationObserver不监听自己导致的变化
      adhesionDistance: 30, //配置的粘附距离
      widthList: [],
      showLineR: false,
      showLineB: false,
      heightList: [],
      breadArr: [
        { name: "我的报表" },
        { name: "报表列表", url: "/business/reportmgr/customReportList" },
        {
          name: "报表编辑",
          url: "/business/reportmgr/dragReport/" + this.chartLayoutName,
        },
      ],
      comKey: "",
      isDropRow: false,
      changeSize: "",
      changeData: "",
      isLoading: false,
      dialog: false,
      colData: {},
      rowData: {},
      showTitleEditer: false,
      dragReportData: {
        title: "",
        children: [],
      },
      // 拖动 row 和 col 的内容存储地
      dragData: {
        //拖动尺寸条
        isResize: false,
        // 拖动的如果是 row
        isRow: false,
        // 拖动的是新的 row
        isNewRow: false,
        // 拖动的是新的 col
        isNewCol: false,
        // col 信息
        col: null,
        // row 信息
        row: null,
      },
      addRow: {
        show: false,
      },
      addCol: {
        show: false,
      },
      menuDatasOri: {},
      menuData: {},
      // 删除行/列时缓存id及对应DOM
      deleteRows: [],
      deleteCols: {},
    };
  },
  watch: {
  },
  mounted() {
    this.getReportData();
  },
  beforeDestroy() {
    if (observer.disconnect) {
      observer.disconnect();
    }
  },
  methods: {
    checkIsTitle({ componentName, height }) {
      let isTitleBox = false;
      //主要是判断是不是padding：0
      if (
        componentName !== "title-title" &&
        componentName !== "text-description"
      ) {
        if (!componentName) {
          if (height <= 100 && height != 0) {
            isTitleBox = true;
          }
        }
      } else {
        isTitleBox = true;
      }
      return isTitleBox;
    },
    changeComTab(tab, col) {
      this.changeSize = new Date();
      col.mainTitle = tab.label;
    },
    resizeStart(e) {
      this.x = e.pageX;
      this.y = e.pageY;
      let resizeDom = $(e.target).parent();
      this.domWidth = resizeDom.innerWidth();
      this.domHeight = resizeDom.innerHeight();
      let dragStyle = new Image({ width: 0, height: 0 });
      e.dataTransfer.setDragImage(dragStyle, 0, 0);
      e.dataTransfer.effectAllowed = "move";
    },
    //拖拽右侧宽度条
    dragRight(e, colData) {
      let dragStyle = new Image({ width: 0, height: 0 });
      e.dataTransfer.setDragImage(dragStyle, 0, 0);
      if (pageX === e.pageX) {
        return;
      }
      pageX = e.pageX;
      let resizeDom = $(e.target).parent();
      $(".auxiliary-line.ri").css({
        left:
          ((resizeDom.innerWidth() + resizeDom.position().left + 7.5) /
            $(".layout-row").width()) *
            100 +
          "%",
      });
      let width = this.domWidth + e.pageX - this.x;
      //设置宽度加上border margin padding共57px
      let padBorWid = 42;
      let padHei = 12;
      let pad0 = false;
      if (
        this.checkIsTitle({
          componentName: colData.componentName,
          height: resizeDom.height(),
        })
      ) {
        padBorWid = 2;
        padHei = 0;
        pad0 = true;
      }
      resizeDom.css({
        height: this.domHeight - padHei,
        width: width - padBorWid,
        padding: pad0 ? "0px" : "0 20px 12px",
      });
      this.dragData.isRow = false;
      this.dragData.isResize = true;
    },
    //拖拽下侧宽度条
    dragBottom(e, colData) {
      if (pageY === e.pageY) {
        return;
      }
      pageY = e.pageY;
      let resizeDom = $(e.target).parent();
      let width = resizeDom.outerWidth();
      $(".auxiliary-line.bo").css({
        top: resizeDom.innerHeight() + resizeDom.position().top + 5,
      });
      let height = this.domHeight + e.pageY - this.y;
      let padWid = 40;
      let padHei = 12;
      let pad0 = false;
      if (this.checkIsTitle({ componentName: colData.componentName, height })) {
        padWid = 0;
        padHei = 0;
        pad0 = true;
      }
      resizeDom.css({
        height: height - padHei,
        width: this.domWidth - padWid,
        padding: pad0 ? "0px" : "0 20px 12px",
      });
      this.dragData.isRow = false;
      this.dragData.isResize = true;
    },
    previewColStyle(
      {
        col,
        width,
        height,
        baseWidth = 100,
        baseHeight = 3,
        layoutRow = 24,
        componentName,
      },
      mixinStyle = {}
    ) {
      let padding = "0 20px 12px";
      if (
        componentName !== "title-title" &&
        componentName !== "text-description"
      ) {
        if (componentName) {
          width = `calc(${(baseWidth * width) / layoutRow}% - 57px `;
        } else {
          if (height <= 100 && height != 0) {
            width = `calc(${(baseWidth * width) / layoutRow}% - 17px `;
            padding = "0px";
          } else {
            width = `calc(${(baseWidth * width) / layoutRow}% - 57px`;
          }
        }
      } else {
        width = `calc(${(baseWidth * width) / layoutRow}% - 17px `;
        padding = "0px";
      }
      return {
        width,
        padding: padding,
        height: `${height / baseHeight}px`,
        ...mixinStyle,
      };
    },
    moveRow({ rowData, type }) {
      this.handleDragRow({}, rowData);
      let nextRow;
      if (type == "up") {
        nextRow = this.dragReportData.children.find(
          (item) => item.index === rowData.index - 1
        );
      } else {
        nextRow = this.dragReportData.children.find(
          (item) => item.index === rowData.index + 1
        );
      }
      this.handleDropRow(event, nextRow);
    },
    /**
     * 行/列设置changeType, 保存接口行/列更新时使用
     * @param {} el 对象元素
     * @param string changeType 'add'-新增; 'edit'-修改; 'delete'-删除
     */
    setElChangeType(el, changeType) {
      // el.changeType = changeType
      el["changeType"] = changeType;
    },
    /**
     * 判断元素是否为新增的行/列
     * @param  {}  el
     * @return Boolean  true-是; false-否
     */
    isElAdded(el) {
      return el.changeType === "add";
    },
    cacheDeleteCol(el) {
      if (!this.isElAdded(el)) {
        delete el.api;

        this.setElChangeType(el, "delete");
        if (!this.deleteCols[el.rowId]) {
          this.deleteCols[el.rowId] = {};
        }

        this.deleteCols[el.rowId][el.styleModuleId] = el;
      }
    },
    // 拖动新的 row
    handleDragNewRow(event, row) {
      this.dragData.isRow = true;
      this.addRow.show = false;
      this.dragData.isNewRow = true;
      this.dragData.row = getInitRow(row);
      this.setElChangeType(this.dragData, "add");
    },
    // 拖动已存在的 row 重排时
    handleDragRow(event, row) {
      this.dragData.isRow = true;
      this.dragData.isNewRow = false;
      this.dragData.row = row;
    },
    pushDropRow(event, drapRow) {
      event.stopPropagation();
      if (!this.dragData.isRow) return;
      const { row: dragRow } = this.dragData;
      const { children: rows } = this.dragReportData;
      if (this.dragData.isNewRow) {
        if (drapRow) {
          drapRow.children.push(...dragRow.children);
        }
        this.addRow.show = true;
      }
    },
    // 对 dropRow 的函数处理
    handleDropRow(event, drapRow) {
      event.stopPropagation();
      if (!this.dragData.isRow) return;
      const { row: dragRow } = this.dragData;
      const { children: rows } = this.dragReportData;
      if (!this.dragData.isNewRow) {
        // 如果拖拽的不是新的 row
        // 则交换两个 row 的 index，并对 rows 进行 sort
        [dragRow.index, drapRow.index] = [drapRow.index, dragRow.index];

        this.setElChangeType(dragRow, "edit");
        // 如果被交换的行, 已经过保存操作实例化
        drapRow.styleModuleId && this.setElChangeType(drapRow, "edit");

        this.sortRows();
      } else {
        // 如果拖拽的是新的 row
        this.setElChangeType(dragRow, "add");
        // 找出 drapRow 后面的所有 rows，并把他们的 index + 1
        // 为的是拖拽放置的时候能放置在被 drap 的 row 后面
        if (drapRow) {
          rows.forEach((row) => {
            if (row.index > drapRow.index) {
              row.index++;
              row.id && this.setElChangeType(row, "edit");
            }
          });
          rows.push(mixinObjs(dragRow, { index: drapRow.index - 1 }));
        } else {
          rows.push(mixinObjs(dragRow, { index: rows.length }));
        }
        this.sortRows();
        this.addRow.show = true;
      }
    },
    // 重排 rows，并会对 index 重新赋值
    sortRows() {
      const { children: rows } = this.dragReportData;
      rows.sort(diffIndex);
      rows.forEach(resetIndex);
      this.isDropRow = true;
    },
    // 删除某 row
    handleRemoveRow(row) {
      this.$confirm("是否确定删除整行布局?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          const { children: rows } = this.dragReportData;
          let spliceRow = rows.splice(row.index, 1)[0];
          // 如果不是新的 row 将删除 row缓存起来, 待保存布局时合并
          if (!this.isElAdded(spliceRow)) {
            this.setElChangeType(spliceRow, "delete");
            spliceRow["children"] = [];
            this.deleteRows.push(spliceRow);
          }

          this.sortRows();
          this.$message({
            type: "success",
            dangerouslyUseHTMLString: true,
            message:
              '<ul class="message"><li>删除成功, 请单击</li><li><img src="../../../../../static/images/reportMgr/icon/save.svg"/></li><li>按钮保存生效!</li></ul>',
          });
        })
        .catch(() => {});
    },
    // edit col functions
    handleDragOver(event) {
      // 默认情况下，对于 drop 的元素，要使用 dragover 移除默认事件，event.preventDefault()
      event.preventDefault();
    },
    // 拖动一个新的 col
    handleDragNewCol(event, col) {
      this.dragData.isRow = false;
      this.dragData.isNewCol = true;
      this.dragData.col = col;
      // 把布局盒子隐藏，方便排版
      this.addCol.show = false;
    },
    // 拖动一个已存在布局中的 col
    handleDragCol(event, col) {
      this.dragData.isRow = false;
      this.dragData.isNewCol = false;
      this.dragData.col = col;
    },
    // 放置一个 col
    handleDropCol(event, col, row) {
      //拖动新组件时，重新赋予key值，防止组件复用产出bug
      // 如果拖动的是 row，不用管
      if (this.dragData.isResize) {
        this.dragData.isResize = false;
        return;
      }
      this.comKey = JSON.stringify(new Date());
      if (this.dragData.isRow) {
        this.handleDropRow(event, row);
        return;
      }
      const { col: dragCol } = this.dragData;
      let cacheDragCol = deepClone(dragCol),
        dragRowId = cacheDragCol.rowId;
      let cacheCol = deepClone(col),
        colRowId = cacheCol.rowId;
      if (!this.dragData.isNewCol) {
        // 若拖动的 col 不是新 col
        // 有两种情况要考虑，一个是放置的位置没有 col
        // 一个是放置的位置有 col
        if (col.previewImage) {
          // 若放置的位置不是空的，则将两者交换
          this.switchColPos(dragCol, col);
        } else {
          // 若放置的位置是空的，
          // 有两种情况要考虑
          // 一个是空位置经过保存操作已实例化 col.styleModuleId 有值且当前页面唯一，则将两者交换
          if (col.styleModuleId) {
            this.switchColPos(dragCol, col);
            this.resetCol(dragCol, true);
          }
          // 若放置的位置是空的，则将 col 赋值并重置 from
          else {
            this.resetColPos(dragCol, col);
            this.resetCol(dragCol, true);
          }
        }
      } else {
        // 若拖动的 col 是新的 col
        this.setNewColPos(dragCol, col);
        this.addCol.show = true;
      }
      setTimeout(() => {
        this.dragData.col = null;
        this.dragData.row = null;
      });
    },
    // 两个 col 位置互换
    switchColPos(dragCol, col) {
      let cacheDragCol = deepClone(dragCol),
        dragRowId = cacheDragCol.rowId;
      let cacheCol = deepClone(col),
        colRowId = cacheCol.rowId;

      // 不同行之间组件交换，先缓存删除的原行组件
      if (cacheDragCol.rowId !== cacheCol.rowId) {
        this.cacheDeleteCol(deepClone(cacheDragCol));
        this.cacheDeleteCol(deepClone(cacheCol));
        cacheDragCol.rowId = colRowId;
        cacheCol.rowId = dragRowId;

        this.setElChangeType(cacheDragCol, "add");
        this.setElChangeType(cacheCol, "add");
      }
      // 同行之间组件交换
      else {
        this.setElChangeType(cacheDragCol, "edit");
        this.setElChangeType(cacheCol, "edit");
      }

      // 拖动的col未实例化 需清空目标位置已实例化数据
      if (!cacheDragCol.id) {
        delete col.id;
        delete col.index;
        delete col.styleModuleId;
      }

      this.setCol(cacheDragCol, col);
      this.setCol(cacheCol, dragCol);
    },
    // col 置于空白位置
    resetColPos(dragCol, col) {
      let cacheDragCol = deepClone(dragCol);
      let cacheCol = deepClone(col);

      // 如果拖动的组件, 经过保存操作已实例化
      if (cacheDragCol.previewImage) {
        this.cacheDeleteCol(deepClone(cacheDragCol));
        delete cacheDragCol.rowId;

        this.setElChangeType(cacheDragCol, "add");
        this.setElChangeType(cacheCol, "add");

        delete dragCol.id;
        delete dragCol.index;
        delete dragCol.styleModuleId;

        this.setCol(cacheDragCol, col);
        this.setCol(cacheCol, dragCol);
      } else {
        this.setElChangeType(cacheDragCol, "add");
        this.setCol(cacheDragCol, col);
      }
    },
    // 新拖动 col 设置位置
    setNewColPos(dragCol, col) {
      let cacheDragCol = deepClone(dragCol);
      let cacheCol = deepClone(col);

      // 两种情况：一是即将放置位置已有组件 即经过保存操作已实例化（包括空白）
      // 需要先缓存被替换的组件
      if (cacheCol.previewImage || cacheCol.styleModuleId) {
        this.cacheDeleteCol(cacheCol);
      }
      // 一是放置位置无实例化组件为空 只需要将 col 赋值即可
      this.setElChangeType(cacheDragCol, "add");

      delete col.id;
      delete col.index;
      delete col.styleModuleId;

      this.setCol(cacheDragCol, col);
    },
    delCol(col) {
      this.cacheDeleteCol(deepClone(col));

      for (const key in col) {
        if (col.hasOwnProperty(key)) {
          if (colSkipArr.indexOf(key) === -1) {
            delete col[key];
          }
        }
      }
    },
    setCol(from, to) {
      for (const key in from) {
        if (from.hasOwnProperty(key)) {
          if (colSkipArr.indexOf(key) === -1) {
            to[key] = from[key];
          }
        }
      }
    },
    resetCol(col, isMove) {
      if (!col) return;
      if (isMove) {
        let init = getInitCol({
          initCol: col.initCol,
        });
        col.id && (init.id = col.id);
        col.changeType && (init.changeType = col.changeType);
        col.index && (init.index = col.index);
        col.styleModuleId && (init.styleModuleId = col.styleModuleId);

        this.delCol(col);
        this.setCol(init, col);
      } else {
        this.$confirm("是否删除此组件?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            let init = getInitCol({
              initCol: col.initCol,
            });
            this.setElChangeType(init, "add");

            this.delCol(col);
            this.setCol(init, col);
            this.$message({
              type: "success",
              dangerouslyUseHTMLString: true,
              message:
                '<ul class="message"><li>删除成功, 请单击</li><li><img src="../../../../../static/images/reportMgr/icon/save.svg"/></li><li>按钮保存生效!</li></ul>',
            });
          })
          .catch(() => {});
      }
    },
    //为了触发chart里resize()方法
    resize() {
      this.changeSize = new Date();
    },
    rowStyle(row) {
      return {
        justifyContent: row.align,
        height: `${row.initHeight == 0 ? 300 : row.initHeight} px`,
      };
    },
    handleEditTitle() {
      this.showTitleEditer = true;
      this.$nextTick(() => {
        let { titleInput } = this.$refs;
        titleInput.focus();
      });
    },
    setStyle(col, row) {
      if (!col.componentName || col.dataKey !== "common") {
        return;
      }
      this.dialog = true;
      this.colData = col;
      this.rowData = row;
      this.resize();
    },
    changeType() {
      this.colData.styleModuleId && this.setElChangeType(this.colData, "edit");
    },
    changeForm(styleForm) {
      this.colData.componentData = JSON.parse(
        JSON.stringify(styleForm || this.colData.componentData)
      );
      this.colData.styleModuleId && this.setElChangeType(this.colData, "edit");
      //this.resize()
    },
    changeApi(api) {
      this.colData.api = api || this.colData.api;
      // 支持数据源配置多个标签场景, 如 dsId "1,2", labels "标签1,标签2"
      this.colData.dsId = this.getPropArr(this.colData.api, "dsId");
      this.colData.labels = this.getPropArr(this.colData.api, "label");

      this.colData.styleModuleId && this.setElChangeType(this.colData, "edit");
      this.resize();
    },
    // 从api对象中获取指定属性数据值
    getPropArr(api, propName) {
      if (!api) {
        return "";
      }

      let propArr = [];
      // 数据源配置多个标签
      if (Array.isArray(api)) {
        propArr = api.map((item) => item[propName]);
      }
      // 数据源配置一个标签
      else {
        propArr.push(api[propName]);
      }
      return propArr.join(",");
    },
    getReportData() {
      this.isLoading = true;
      nt.doRequest({
        method: "post",
        uri: "/paas/chart-layout/v2/chart/getByName",
        data: {
          params: this.chartLayoutName,
        },
        onSuccess: (resp) => {
          let dragReportData = {};
          if (resp.data.content) {
            dragReportData = resp.data.content;
          }

          dragReportData.children = dragReportData.children || [];
          dragReportData.children.forEach((rowItem) => {
            rowItem.children.forEach((colItem) => {
              //下面是为了兼容已上线配置没有字段的情况
              colItem.offsetX = colItem.offsetX || 0;
              colItem.colHeight =
                colItem.colHeight || parseInt(rowItem.initHeight);
              if (typeof colItem.componentData === "object") {
                colItem.componentData.linkText =
                  colItem.componentData.linkText || "详情";
              } else {
                colItem.componentData = { url: [] };
              }

              if (!colItem.api || !colItem.api.length) {
                colItem.api = null;
                colItem.dsId = null;
              } else {
                colItem.api =
                  colItem.api.length === 1 ? colItem.api[0] : colItem.api;
              }

              // 预设用于缓存删除 col 组件
              colItem.rowId = rowItem.id;
            });
          });
          this.dragReportData = dragReportData;
          this.isLoading = false;
          this.$nextTick(() => {
            //只有第一次调用的时候进行宽度高度判断
            this.markPosition();
          });
        },
        onError: (resp) => {
          this.isLoading = false;
        },
      });
    },
    markPosition() {
      this.widthList = [];
      this.heightList = [];
      $(".layout-col-box").each((i, item) => {
        this.widthList.push(item.offsetLeft + item.offsetWidth);
        this.heightList.push(item.offsetTop + item.offsetHeight);
      });
    },
    changeTableProp(colComponentData, tableComponentData) {
      colComponentData.tableColData = tableComponentData;
    },
    changeTitleTooltip(colComponentData, tooltipList) {
      colComponentData.tooltipList = tooltipList;
    },
    changeSeriesTypeList(colComponentData, seriesTypeList) {
      colComponentData.seriesTypeList = seriesTypeList;
    },
    changeSeriesYAxisIndexList(colComponentData, seriesYAxisIndexList) {
      colComponentData.seriesYAxisIndexList = seriesYAxisIndexList;
    },
    changeBarSeriesStackList(colComponentData, seriesStackList) {
      colComponentData.seriesStackList = seriesStackList;
    },
    changeColYAxis(colComponentData, yAxis) {
      colComponentData.yAxis = yAxis;
    },
    changeBarLabelList(colComponentData, labelList) {
      colComponentData.series[0].label = labelList;
    },
    saveComponent() {
      nt.doRequest({
        method: "post",
        uri: "/paas/chart-layout/v1/chart/component/save",
        data: {
          params: {
            id: this.colData.id,
            mainTitle: this.colData.mainTitle,
            componentName: this.colData.componentName,
            component: JSON.stringify(this.colData),
          },
        },
        onSuccess: (resp) => {
          this.colData.id = resp.data.content;
          this.$message.success("保存成功");
          if (this.$refs.framework) {
            this.$refs.framework.getUserComponentDatas();
          }
        },
      });
    },
    /**
     * 格式化需保存/预览的数据对象
     * @return {[type]} [description]
     */
    formatReportData() {
      let that = this;
      let deleteCols = that.deleteCols;
      let chartLayoutContent = JSON.parse(JSON.stringify(that.dragReportData));
      chartLayoutContent.children.forEach((rowItem, i) => {
        rowItem.children.forEach((colItem, j) => {
          colItem.index = j;

          // 组件移动到其他行，再移动回原行
          if (
            deleteCols[rowItem.id] &&
            deleteCols[rowItem.id][colItem.styleModuleId]
          ) {
            colItem.styleModuleId && that.setElChangeType(colItem, "edit");
            delete deleteCols[rowItem.id][colItem.styleModuleId];
          }

          // 查询数据时预设 需删除
          delete colItem.rowId;

          if (colItem.api && !Array.isArray(colItem.api)) {
            // 组件为其他表格类型api硬编码到代码中
            if (typeof colItem.api === "string") {
              delete colItem.api;
            } else {
              colItem.api = [colItem.api];
            }
          }
        });

        // 重置，便于后台保存数据
        rowItem.index = i;

        // 合并本行已删除组件，后台用于清空关联关系
        if (deleteCols[rowItem.id]) {
          let tmpCols = deleteCols[rowItem.id];
          rowItem.children.push(...Object.keys(tmpCols).map((k) => tmpCols[k]));
        }
      });

      // 合并已删除行，后台用于清空布局及其下所有组件信息
      chartLayoutContent.children.push(...that.deleteRows);
      return chartLayoutContent;
    },
    handleToPreviewPage() {
      setStorage("drag-report-data", JSON.stringify(this.formatReportData()));
    },
    handleSaveLayout() {
      nt.doRequest({
        method: "post",
        uri: "paas/chart-layout/v2/chart/chartLayout/save",
        data: {
          params: this.formatReportData(),
        },
        onSuccess: (response) => {
          // 后台接口新增Row和Col时设置id属性，需刷新页面数据
          this.getReportData();

          this.deleteRows = [];
          this.deleteCols = {};
          this.$message.success(response.data.content);
          this.$emit( "save-success");
        },
      });
    },
    handleReturnLayout() {
      this.$confirm("此操作将不保存已编辑内容，是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$emit( "save-success");
        })
        .catch(() => {});
    }
  },
  beforeUpdate() {
    this.$arrRefs && (this.$arrRefs.arr = []);
  },
  emits: ["save-success"],
};
</script>

<style>
.opacityModel{
background-color: transparent;
}
.opacityModel .el-drawer {
  overflow-y: auto;
}
.report-container .el-tabs__item {
  height: 25px;
  line-height: 25px;
}
.report-container .el-tabs__nav-next,
.report-container .el-tabs__nav-prev {
  line-height: 25px;
}
.report-container .el-tabs--border-card > .el-tabs__content {
  padding: 0;
  height: calc(100% - 35px);
}
.report-container .el-tabs--border-card > .el-tabs__content .el-tab-pane {
  height: 100%;
}
.el-message__content ul.message > li {
  float: left;
  height: 46px;
  line-height: 46px;
}
</style>

<style lang="scss" scoped>
.tool-box {
  position: fixed;
  z-index: 9999;
  transform: translateX(240px);
  right: 0;
  top: 250px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  transition: all 0.8s;
  width: 290px;
  height: 65px;
  background: #ffffff;
  box-shadow: 0 4px 6px 2px rgba(0, 0, 0, 0.1);
  border-radius: 32.5px 0 0 32.5px;
  &.drag {
    transform: translateX(0px);
  }
  &:hover {
    transform: translateX(0px);
  }
}
.detail-btn {
  position: absolute;
  cursor: pointer;
  right: 20px;
  top: 15px;
  color: #419eff;
}

.auxiliary-line {
  position: absolute;
  z-index: 99;

  &.ri {
    height: 100%;
    width: 2px;
    top: 0;
    left: 0;
    border-right: 1px dashed #6e40aa;
  }
  &.bo {
    height: 2px;
    width: 100%;
    left: 0;
    top: 0;
    border-bottom: 1px dashed #6e40aa;
  }
}

.el-tabs {
  width: 100%;
  height: 100%;
  border: none;
  box-shadow: none;
}

.el-tabs__header.is-bottom {
  margin-top: 0;
}

.removeButton {
  position: absolute;
  right: 15px;
  top: 15px;
}

.demo-drawer__footer > button {
  float: right;
  margin: 10px 5px;
}

.col-box {
  position: relative;
  padding: 0 20px 12px;
  margin: 0 7.5px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  background: #fff;
  overflow: hidden;
  resize: both;

  .resize-border {
    position: absolute;
    z-index: 9;
    /*background: yellow;*/
    &.ri {
      height: 30px;
      width: 10px;
      right: -5px;
      top: calc(50% - 15px);
      cursor: e-resize;
    }
    &.bo {
      height: 10px;
      width: 30px;
      left: calc(50% - 15px);
      bottom: -5px;
      cursor: s-resize;
    }
  }

  .resize-btn {
    display: none;
    cursor: nw-resize;
    height: 15px;
    width: 15px;
    position: absolute;
    right: 0;
    bottom: 0;
    background: #6e40aa;
  }
}

.pad0 {
  padding: 0;
}

.col-title {
  padding: 18px 0px 10px;
}

.main-front {
  font-size: 14px;
  color: #6d7c8b;
  font-weight: bold;
}

.secondary-front {
  font-size: 12px;
  color: #a3adb6;
}

.report-container {
  .edit {
    display: none;
    position: absolute;
    top: 5px;
    right: 45px;
    z-index: 5;
  }
  .col-box:hover .edit {
    display: block;
  }

  .el-col {
    height: 100%;
    display: flex;
    align-items: center;
    .el-form-item {
      margin-bottom: 0 !important;
    }
  }

  .remove {
    display: none;
    position: absolute;
    top: 5px;
    right: 5px;
    z-index: 5;
  }
}

@import "./layout.scss";
</style>
